/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.engine.repository.util;

import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.bef.engine.entity.AssociationInfo;
import com.inspur.edp.bef.engine.repository.exception.BefRespositoryException;
import com.inspur.edp.bef.engine.repository.exception.ErrorCodes;
import com.inspur.edp.cef.api.repository.INestedRepository;
import com.inspur.edp.cef.api.repository.readerWriter.ICefReader;
import com.inspur.edp.cef.api.utils.GeometryUtil;
import com.inspur.edp.cef.core.crypt.EncryptManager;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.collection.GspFieldCollection;
import com.inspur.edp.cef.designtime.api.element.EnumIndexType;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.designtime.api.element.GspEnumValue;
import com.inspur.edp.cef.entity.changeset.ValueObjModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.EntityDataPropertyValueUtils;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.repository.readerwriter.CefMappingReader;
import com.inspur.edp.commonmodel.engine.api.common.CMEngineUtil;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.udt.api.UdtManagerUtils;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.dbInfo.ColumnMapType;
import com.inspur.edp.udt.entity.ISimpleUdtData;
import io.iec.edp.caf.commons.utils.StringUtils;
import lombok.var;
import org.eclipse.core.internal.utils.Convert;
import org.locationtech.jts.geom.Geometry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.sql.Clob;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

public class ReadUtils {
    protected static Logger logger = LoggerFactory.getLogger(ReadUtils.class);
    private static SimpleDateFormat sdfForYYYYMMDD = new SimpleDateFormat("yyyyMMdd");

    /**
     * 获取实体上的字段值
     * @param element
     * @param reader
     * @return
     */
    public static Object getValue(IGspCommonElement element, ICefReader reader) {
        if (element.getObjectType() == GspElementObjectType.Association)
            //最外层的关联
            return getAssoValue(element, reader);
        Object value = reader.readValue(element.getLabelID());
        return getObjNormalValue(element, value);
    }

    /**
     * 获取最外层带出字段的值
     * @param current 当前关联字段的标签=outAssoElement.getLabelID()
     * @param outAssoElement 最外层关联字段
     * @param element 带出字段
     * @param reader
     * @return
     */
    public static Object getFirstLevelValue(String current, IGspCommonElement outAssoElement,  IGspCommonElement element, ICefReader reader) {
        if (element.getObjectType() == GspElementObjectType.Association){
            return getRefAssoValue(current, outAssoElement, element, reader, true);
        }

        //如果带出字段是带出字段,需要继续分析
        Object value =  reader.readValue(element.getLabelID());
        return getObjNormalValue(element, value);
    }


    /**
     * 获取内层关联带出字段值
     * @param prefix 前缀集合,通过&分割
     * @param current 当前前缀
     * @param element 带出字段
     * @param sameElement 带出字段对应外层字段
     * @param reader
     * @return
     */
    public static Object getValue(String prefix, String current, IGspCommonElement outAssoElement, IGspCommonElement element, IGspCommonElement sameElement, ICefReader reader) {
        if (element.getObjectType() == GspElementObjectType.Association){
            //进入这的，prefix应该肯定有值
            if(StringUtils.isEmpty(prefix)){
                prefix = current;
            }
            else {
                prefix += "&" + current;
            }
            //此处需要获取第三层及以上的关联字段
            return getRefAssoValue(prefix, outAssoElement, element, reader, false);
        }


        //如果带出字段是带出字段,需要继续分析
        Object value = null;
        String elementPrefix = "";
        String[] array = prefix.split("&");
        List<String> listPrefix = Arrays.asList(array);
        if(sameElement == null){
            for(int i = 0; i< listPrefix.size(); i++){
                elementPrefix += listPrefix.get(i) + "_";
            }
            String mappingLabelId = getAssoRefFieldMapping(outAssoElement, element);
            if(StringUtils.isEmpty(mappingLabelId)){
                mappingLabelId = elementPrefix + element.getLabelID();
            }
            //带出字段的迎合规则为：上级标签_上级标签_当前字段标签
            value = reader.readValue(mappingLabelId);
        }
        else{
            //如果外层有带出字段，则按照外层的规则来取数
            if(listPrefix.size() > 1){
                for(int i = 0; i< listPrefix.size() - 1; i++){
                    elementPrefix += listPrefix.get(i) + "_";
                }
            }
            String mappingLabelId = getAssoRefFieldMapping(outAssoElement, element);
            if(StringUtils.isEmpty(mappingLabelId)){
                mappingLabelId = elementPrefix + element.getLabelID();
            }
            //带出字段的迎合规则为：上级标签_上级标签_当前字段标签
            value = reader.readValue(mappingLabelId);
        }

        return getObjNormalValue(element, value);
    }

    /**
     * 获取字段对象值
     *
     * @param element
     * @param value
     * @return
     */
    private static Object getObjNormalValue(IGspCommonElement element, Object value) {
        if (element.getObjectType() == GspElementObjectType.Enum)
            return getEnumValue(element, value);
        switch (element.getMDataType()) {
            case String: {
                String elementValue = getStringValue(element, value);
                return decryptIfEnabled(element, elementValue);
            }
            case Integer:
                return getIntValue(element, value);
            case Decimal:
                return getDecimalValue(element, value);
            case Boolean:
                return getBoolValue(element, value);
            case Date:
            case DateTime:
                return getDateValue(element, value);
            case Text:
                String textValue = getTextValue(element, value);
                return decryptIfEnabled(element, textValue);
            case Binary:
                return getBinaryValue(element, value);
            case Geometry:
                return getGeometryValue(element, value);
        }
        throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2004,
                element.getLabelID(),
                String.valueOf(element.getMDataType()),
                ExceptionUtil.getBEInfo(element.getBelongObject()),
                ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()),
                ExceptionUtil.getBizElementInfo(element));
    }

    /**
     * 获取枚举字段的对象值()
     *
     * @param element
     * @param value
     * @return
     */
    private static Object getEnumValue(IGspCommonElement element, Object value) {
        if (element.getContainEnumValues() == null) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2005, false,
                    element.getLabelID(),
                    ExceptionUtil.getBEInfo(element.getBelongObject()),
                    ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()),
                    ExceptionUtil.getBizElementInfo(element));
        }

        GspEnumValue defaultEnumValue = null;
        HashMap<String, GspEnumValue> stringIndexEnumValue = new HashMap<>();
        HashMap<Integer, GspEnumValue> intIndexEnumValue = new HashMap<>();
        for (GspEnumValue enumValue : element.getContainEnumValues()) {
            if (enumValue.getIsDefaultEnum())
                defaultEnumValue = enumValue;
            if (element.getEnumIndexType() == EnumIndexType.String)
                stringIndexEnumValue.put(enumValue.getStringIndex(), enumValue);
            else
                intIndexEnumValue.put(enumValue.getIndex(), enumValue);
        }

        if (value == null || "".equals(value.toString().trim())) {
            return null;
        }

        String indexValue = null;
        GspEnumValue enumInfo = null;
        if (element.getEnumIndexType() == EnumIndexType.String) {
            String index = getStringValue(element, value);
            enumInfo = stringIndexEnumValue.get(index);
            indexValue = index;
        } else {
            int index = getIntValue(element, value);
            enumInfo = intIndexEnumValue.get(index);
            indexValue = String.valueOf(index);
        }

        if (enumInfo == null) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2006, false, element.getLabelID(), value.toString(),
                    ExceptionUtil.getBEInfo(element.getBelongObject()), ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()),
                    ExceptionUtil.getBizElementInfo(element));
        }
        return enumInfo.getValue();
    }

    /**
     * 获取关联字段的值:第一层
     * @param element
     * @param reader
     * @return
     */
    private static Object getAssoValue(IGspCommonElement element, ICefReader reader) {
        if (element.getChildAssociations() == null || element.getChildAssociations().isEmpty())
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2023,
                    element.getLabelID(),
                    ExceptionUtil.getBEInfo(element.getBelongObject()),
                    ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()));
        AssociationInfo info = new AssociationInfo(element.getChildAssociations());
        info.setValue(element.getLabelID(), getStringValue(element, getStringValue(element, reader.readValue(element.getLabelID()))));
        // 遍历所有关联处理带出字段
        for (GspAssociation association : info.getAssociations()) {
            if (association.getRefElementCollection() == null || association.getRefElementCollection().isEmpty())
                continue;
            for (var refElement : association.getRefElementCollection()) {
                if (refElement.getIsUdt()) {
                    info.setValue(refElement.getLabelID(), ReadUtils.getUdtData(true, reader, (IGspCommonElement) refElement));
                } else {
                    // 最外层关联，前缀是空，current是关联字段本身
                    info.setValue(refElement.getLabelID(), getFirstLevelValue(element.getLabelID(), element, (IGspCommonElement) refElement, reader));
                }
            }
        }

        //TODO 关联后续处理
        return info;
    }

    /**
     * 获取内层关联字段的值
     * @param prefix 前缀,上下级通过&分割
     * @param outAssoElement 最外层的关联字段，用于匹配最外层带出字段是否与当前带出字段一致
     * @param element 带出字段
     * @param reader
     * @param isSecondLevel 是否第二层关联（否则为三层及以上）
     * @return
     */
    private static Object getRefAssoValue(String prefix, IGspCommonElement outAssoElement, IGspCommonElement element, ICefReader reader, boolean isSecondLevel) {
        GspBusinessEntity gspBusinessEntity = CMEngineUtil.getMetadataContent(element.getParentAssociation().getRefModelID());
        GspBizEntityObject gspBizEntityObject = gspBusinessEntity.getNode(element.getParentAssociation().getRefObjectCode());

        IGspCommonField gspCommonField = null;
        for (IGspCommonField field : gspBizEntityObject.getAllElementList(true)) {
            if (field.getID().equals(element.getRefElementId())) {
                gspCommonField = field;
                break;
            }
        }
        GspAssociation association = gspCommonField.getChildAssociations().get(0);
        if (association == null)
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2023,
                    element.getLabelID(),
                    ExceptionUtil.getBEInfo(gspBizEntityObject),
                    ExceptionUtil.getBizEntityObjInfo(gspBizEntityObject));
        AssociationInfo info = new AssociationInfo(association);
        //对于第二层，如果没有匹配到，按照当前字段标签来
        if(isSecondLevel){
            String mappingLabelId = getAssoRefFieldMapping(outAssoElement, element);
            if(StringUtils.isEmpty(mappingLabelId)){
                mappingLabelId = element.getLabelID();
            }
            info.setValue(gspCommonField.getLabelID(), getStringValue(element, getStringValue(element, reader.readValue(mappingLabelId))));
        }
        else{
            String[] array = prefix.split("&");
            List<String> listPrefix = Arrays.asList(array);
            String elementPrefix = "";
            for(int i = 0; i< listPrefix.size() - 1; i++){
                elementPrefix += listPrefix.get(i) + "_";
            }
            String mappingLabelId = getAssoRefFieldMapping(outAssoElement, element);
            if(StringUtils.isEmpty(mappingLabelId)){
                mappingLabelId = elementPrefix+element.getLabelID();
            }
            info.setValue(gspCommonField.getLabelID(), getStringValue(element, getStringValue(element, reader.readValue(mappingLabelId))));
        }
        if (association.getRefElementCollection() == null || association.getRefElementCollection().size() < 1)
            return info;

        for (var refElement : association.getRefElementCollection()) {
            if (refElement.getIsUdt()) {
                info.setValue(refElement.getLabelID(), ReadUtils.getUdtData(element, true, reader, (IGspCommonElement) refElement));
            } else {
                //查看带出字段在上级的关联中是否存在，如果存在，用上面带出字段的规则来处理
                IGspCommonField sameElment = getParentLevelElement(refElement, element);
                info.setValue(refElement.getLabelID(),
                        getValue(prefix, gspCommonField.getLabelID(), outAssoElement, (IGspCommonElement) refElement, (IGspCommonElement) sameElment, reader));
            }
        }
        return info;
    }

    /**
     * 根据最外层关联带出字段获取mapping标签，对于IDP推送BE来讲，最外层BE关联带出字段会包含内层的所有带出字段，所以命名要根据最外层带出字段的标签来，
     * 这里根据columnId是否一致来判断
     * 比如有saleDpt销售部门、productDept生产部门 两个关联字段同时关联了Dept表的name字段，针对IDP场景，name带出字段会在外层跟saleDpt、productDept同级，生成saleDpt_name贺productDept_Name
     * 这里用columnId匹配的时候会匹配到第一个，导致后面的取数不正确，所以需要labelId进行修正判断
     * @param outAssoElement
     * @param currentElement
     * @return
     */
    private static String getAssoRefFieldMapping(IGspCommonElement outAssoElement, IGspCommonElement currentElement){
        String mappingLabelId = "";
        GspFieldCollection gspFieldCollection = outAssoElement.getChildAssociations().get(0).getRefElementCollection();
        //外层关联的是同一个字段，如果有多个，表名有个多个关联字段关联的是同一个。
        List<IGspCommonField> sameList = gspFieldCollection.stream().filter(item -> ((IGspCommonElement)item).getColumnID().equals(currentElement.getColumnID())).collect(Collectors.toList());
        if(sameList == null || sameList.size() == 0){
            return "";
        }

        //如果有一个，就用外层的标签
        if(sameList.size() == 1){
            mappingLabelId = sameList.get(0).getLabelID();
        }else {
            List<IGspCommonField> filterList =  sameList.stream().filter(item -> item.getLabelID().contains(currentElement.getLabelID())).collect(Collectors.toList());
            //用标签进行过滤，尽量还是判断准确，使用外层的标签
            if(filterList != null && filterList.size() == 1){
                mappingLabelId = filterList.get(0).getLabelID();
            }
        }
        return mappingLabelId;
    }

    /**
     * 获取外层带出字段与当前字段一致的字段
     * @param refField
     * @param parentLevelElement
     * @return
     */
    private static IGspCommonField getParentLevelElement(IGspCommonField refField, IGspCommonElement parentLevelElement){
        IGspCommonField sameElement = null;
        GspAssociation gspAssociation = parentLevelElement.getParentAssociation();
        for(IGspCommonField field : gspAssociation.getRefElementCollection()){
            if(field.getRefElementId().equals(refField.getID())){
                sameElement = field;
                break;
            }
        }
        return sameElement;
    }


    private static String getStringValue(IGspCommonElement element, Object value) {
        if (value == null)
            return EntityDataPropertyValueUtils.getStringPropertyDefaultValue();
        else if (value.toString().toLowerCase().contains("oracle.sql.clob")) {
            return getClobValue(element, value);
        }
        return value.toString();
    }

    private static Integer getIntValue(IGspCommonElement element, Object value) {
        if (value == null)
            return null;
        try {
            return Integer.valueOf(value.toString());
        } catch (Exception ex) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2024, ex, false,
                    element.getLabelID(),
                    value.toString(),
                    ExceptionUtil.getBEInfo(element.getBelongObject()),
                    ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()),
                    ExceptionUtil.getBizElementInfo(element));
        }
    }

    private static BigDecimal getDecimalValue(IGspCommonElement element, Object value) {
        if (value == null)
            return null;
        if (value instanceof BigDecimal)
            return (BigDecimal) value;
        try {
            return new BigDecimal(value.toString());
        } catch (Exception ex) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2025, ex, false,
                    element.getLabelID(),
                    value.toString(),
                    ExceptionUtil.getBEInfo(element.getBelongObject()),
                    ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()),
                    ExceptionUtil.getBizElementInfo(element));
        }
    }

    private static Boolean getBoolValue(IGspCommonElement element, Object value) {
        if (value == null)
            return null;
        switch (value.toString().toLowerCase()) {
            case "0":
            case "false":
                return false;
            case "1":
            case "true":
                return true;
            default: {
                throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2026, element.getLabelID(), String.valueOf(value),
                        ExceptionUtil.getBEInfo(element.getBelongObject()),
                        ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()),
                        ExceptionUtil.getBizElementInfo(element));
            }
        }
//        return getBoolean(value.toString());
    }

    private static Date getDateValue(IGspCommonElement element, Object value) {
        if (value == null)
            return EntityDataPropertyValueUtils.getDateTimePropertyDefaultValue();
        try {
            if (value instanceof String && ((String) value).length() == 8) {
                return sdfForYYYYMMDD.parse((String) value);
            }
            return (Date) value;
        } catch (Exception ex) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2027, ex, false, element.getLabelID(), value.toString(),
                    ExceptionUtil.getBEInfo(element.getBelongObject()),
                    ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()),
                    ExceptionUtil.getBizElementInfo(element));
        }
    }

    private static String getTextValue(IGspCommonElement element, Object value) {
        if (value == null)
            return EntityDataPropertyValueUtils.getStringPropertyDefaultValue();
        return getClobValue(value);
    }

    private static byte[] getBinaryValue(IGspCommonElement element, Object value) {
        if (value == null)
            return EntityDataPropertyValueUtils.getBinaryPropertyDefaultValue();
        //TODO: 这个地方为啥不走getDbProcessor().getBlobValue
        if (value instanceof byte[])
            return (byte[]) value;
        return Convert.toUTF8(value.toString());
    }

    private static Geometry getGeometryValue(IGspCommonElement element, Object value) {
        if (value == null)
            return null;

        if(value instanceof Geometry)
            return (Geometry)value;

        if(value instanceof byte[]){
            return GeometryUtil.wkb2Geometry((byte[])value);
        }

        if(value instanceof String){
            return GeometryUtil.wkb2Geometry((String)value);
        }
        return null;
    }

    private static String getClobValue(Object obj) {
        if (obj instanceof String) {
            return obj.toString();
        } else {
            try {
                return obj == null ? EntityDataPropertyValueUtils.getStringPropertyDefaultValue() : ((Clob) obj).getSubString(1L, (int) ((Clob) obj).length());
            } catch (Exception e) {
                throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2029, e, false, String.valueOf(obj));
            }
        }
    }

    private static String getClobValue(IGspCommonElement element, Object obj) {
        if (obj instanceof String) {
            return obj.toString();
        } else {
            try {
                return obj == null ? EntityDataPropertyValueUtils.getStringPropertyDefaultValue() : ((Clob) obj).getSubString(1L, (int) ((Clob) obj).length());
            } catch (Exception e) {
                throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2030, e, false, element.getLabelID(), String.valueOf(obj),
                        ExceptionUtil.getBEInfo(element.getBelongObject()),
                        ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()),
                        ExceptionUtil.getBizElementInfo(element));
            }
        }
    }

    public static Object encryptIfEnabled(Object needEncrypt, boolean IsEnableEncryption) {
        if (needEncrypt == null || needEncrypt.toString().equals("")) {
            return needEncrypt;
        }
        if (!IsEnableEncryption)
            return needEncrypt;
        if (!(needEncrypt instanceof String)) {
            needEncrypt = needEncrypt.toString();
        }
        return EncryptManager.getInstance().getEncrypt("").encrypt((String) needEncrypt);
    }

    public static Object decryptIfEnabled(IGspCommonElement element, Object needDecrypt) {
        if (needDecrypt == null || needDecrypt.toString().equals("")) {
            return needDecrypt;
        }
        boolean IsEnableEncryption = element.getIsEnableEncryption();
        if (element.getIsRefElement()) {
            IsEnableEncryption = getRefElement(element) != null ? ((IGspCommonElement) getRefElement(element)).getIsEnableEncryption() : false;
        }
        if (!IsEnableEncryption)
            return needDecrypt;
        if (!(needDecrypt instanceof String)) {
            needDecrypt = needDecrypt.toString();
        }
        try {
            return EncryptManager.getInstance().getEncrypt("").decrypt((String) needDecrypt);
        } catch (IllegalArgumentException e) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2035, e, false, element.getLabelID());
        }
    }

    private static IGspCommonField getRefElement(IGspCommonElement element) {
        if (element.getIsRefElement()) {
            GspBusinessEntity gspBusinessEntity = CMEngineUtil.getMetadataContent(element.getParentAssociation().getRefModelID());
            GspBizEntityObject gspBizEntityObject = gspBusinessEntity.getNode(element.getParentAssociation().getRefObjectCode());
            for (IGspCommonField gspCommonField : gspBizEntityObject.getContainElements()) {
                if (gspCommonField.getID().equals(element.getRefElementId())) {
                    return gspCommonField;
                }
                if (gspCommonField.getObjectType() == GspElementObjectType.Association && gspCommonField.getChildAssociations().size() > 0) {
                    GspAssociation gspAssociation = gspCommonField.getChildAssociations().get(0);
                    if (gspAssociation.getRefElementCollection() == null || gspAssociation.getRefElementCollection().size() == 0) {
                        continue;
                    }
                    for (IGspCommonField field : gspAssociation.getRefElementCollection()) {
                        if (field.getID().equals(element.getRefElementId())) {
                            return getRefElement((IGspCommonElement) field);
                        }
                    }
                }
            }
        }
        return null;
    }
    //endregion

    //region udt字段賦值

    public static void setUdtData(ICefReader reader, IEntityData data, IGspCommonElement element) {
        Object value = getUdtData(reader, element);
        data.setValue(element.getLabelID(), value);
    }

    public static void setUdtData(ICefReader reader, boolean isAssoRef, IEntityData data, IGspCommonElement element) {
        Object value = getUdtData(isAssoRef, reader, element);
        data.setValue(element.getLabelID(), value);
    }

    public static Object getUdtData(ICefReader reader, IGspCommonElement element) {
        return getUdtData(true, reader, element);
    }

    public static Object getUdtData(boolean isAssoRef, ICefReader reader, IGspCommonElement element) {
        UnifiedDataTypeDef udt = CMEngineUtil.getMetadataContent(element.getUdtID());
        if (udt == null)
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2031, false,
                    element.getLabelID(),
                    element.getUdtID(),
                    ExceptionUtil.getBEInfo(element.getBelongObject()),
                    ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()),
                    ExceptionUtil.getBizElementInfo(element));
        Object value = udt instanceof SimpleDataTypeDef ? setSimpleUdtData(null, reader, element,
                (SimpleDataTypeDef) udt) : setComplexUdtData(reader, isAssoRef, element, (ComplexDataTypeDef) udt);
        if (udt instanceof SimpleDataTypeDef && ((ISimpleUdtData) value).getValue() != null) {
            ((ISimpleUdtData) value).setValue(decryptIfEnabled(element, ((ISimpleUdtData) value).getValue()));
        }
        return value;
    }

    public static Object getUdtData(IGspCommonElement assoElment, boolean isAssoRef, ICefReader reader, IGspCommonElement element) {
        UnifiedDataTypeDef udt = CMEngineUtil.getMetadataContent(element.getUdtID());
        if (udt == null)
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2031,
                    element.getLabelID(),
                    element.getUdtID(),
                    ExceptionUtil.getBEInfo(element.getBelongObject()),
                    ExceptionUtil.getBizEntityObjInfo(element.getBelongObject()),
                    ExceptionUtil.getBizElementInfo(element));
        Object value = udt instanceof SimpleDataTypeDef ? setSimpleUdtData(assoElment, reader, element,
                (SimpleDataTypeDef) udt) : setComplexUdtData(reader, isAssoRef, element, (ComplexDataTypeDef) udt);
        if (udt instanceof SimpleDataTypeDef && ((ISimpleUdtData) value).getValue() != null) {
            ((ISimpleUdtData) value).setValue(decryptIfEnabled(element, ((ISimpleUdtData) value).getValue()));
        }
        return value;
    }

    private static Object setSimpleUdtData(IGspCommonElement assoElement, ICefReader reader, IGspCommonElement element, SimpleDataTypeDef udt) {
        LinkedHashMap<String, String> map = new LinkedHashMap();
        switch (udt.getObjectType()) {
            case None:
            case Enum: {
                if (assoElement != null && assoElement.getIsRefElement() && assoElement.getParentAssociation() != null && assoElement.getParentAssociation().getBelongElement() != null) {//处理关联-带出关联-带出UDT字段的场景
                    IGspCommonField parentElement = assoElement.getParentAssociation().getBelongElement();
                    map.put(udt.getCode(), parentElement.getLabelID() + "_" + element.getLabelID());
                } else {
                    map.put(udt.getCode(), element.getLabelID());
                }
                break;
            }
            case Association:
                addAssUdtMapping(map, element, udt);
                break;
            default:
                break;
        }

        return getUdtData(map, reader, udt);
    }

    private static void addAssUdtMapping(LinkedHashMap<String, String> mapping, IGspCommonElement element, SimpleDataTypeDef udt) {
        if (element.getChildAssociations() != null && element.getChildAssociations().size() > 0) {
            var fieldAssociation = element.getChildAssociations().get(0);
            var udtAssociation = udt.getChildAssociations().get(0);
            mapping.put(udt.getCode(), element.getLabelID());
            for (var item : fieldAssociation.getRefElementCollection()) {
                if (!item.getIsFromAssoUdt()) {
                    continue;
                }
                IGspCommonField udtRefField =
                        getElementByRefElementID(udtAssociation, (IGspCommonElement) item, fieldAssociation, udt);
                mapping.put(udtRefField.getLabelID(), item.getLabelID());
                //todo 暂时隐藏，跟其他模块一块提交
                if (udtRefField.getIsUdt()) {
                    //判断是否多值UDT 或者继续？
                    UnifiedDataTypeDef glUdt = CMEngineUtil.getMetadataContent(udtRefField.getUdtID());
                    if (glUdt instanceof ComplexDataTypeDef) {
                        ComplexDataTypeDef complexDataTypeDef = (ComplexDataTypeDef) glUdt;
                        if (complexDataTypeDef.getDbInfo().getMappingType() == ColumnMapType.MultiColumns) {
                            for (IGspCommonField udtField : complexDataTypeDef.getContainElements()) {
                                mapping.put(udtRefField.getLabelID() + "_" + udtField.getLabelID(), item.getLabelID() + "_" + udtField.getLabelID());
                            }
                        }
                    }
                }
            }
        } else {//关联udt
            mapping.put(udt.getCode(), element.getLabelID());
            for (IGspCommonField udtField : udt.getChildAssociations().get(0).getRefElementCollection()) {
                mapping.put(udtField.getLabelID(), element.getLabelID() + "_" + udtField.getLabelID());
            }
        }
    }

    private static IGspCommonField getElementByRefElementID(
            GspAssociation udtAssociation,
            IGspCommonElement gspCommonField,
            GspAssociation fieldAssociation,
            SimpleDataTypeDef udt) {
        for (var item : udtAssociation.getRefElementCollection()) {
            if (gspCommonField.getRefElementId().equals(item.getRefElementId())) {
                return item;
            }
        }
        throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2032, false,
                udt.getCode(),
                udt.getId(),
                ExceptionUtil.getBEInfo((IGspCommonObject) fieldAssociation.getBelongElement().getBelongObject()),
                ExceptionUtil.getBizEntityObjInfo((IGspCommonObject) fieldAssociation.getBelongElement().getBelongObject()),
                ExceptionUtil.getBizElementInfo(fieldAssociation.getBelongElement()),
                ExceptionUtil.getBizElementInfo(gspCommonField));
    }

    private static Object setComplexUdtData(ICefReader reader, boolean isAssoRef, IGspCommonElement element, ComplexDataTypeDef udt) {
        LinkedHashMap<String, String> map = null;
        switch (udt.getDbInfo().getMappingType()) {
            case MultiColumns:
                map = getMultiColumnsUdtMap(reader, isAssoRef, element, udt);
                break;
            case SingleColumn:
                map = getSingleColumnUdtMap(reader, element, udt);
                break;
        }

        return getUdtData(map, reader, udt);
    }

    private static Object getUdtData(
            LinkedHashMap<String, String> map,
            ICefReader reader,
            UnifiedDataTypeDef udt) {
        CefMappingReader mappingReader = new CefMappingReader(map, reader);
        INestedRepository u1Repos = UdtManagerUtils.getUdtRepositoryFactory().createRepository(udt.getUdtType());
        return u1Repos.readData(mappingReader);
    }

    private static LinkedHashMap<String, String> getSingleColumnUdtMap(ICefReader reader, IGspCommonElement element, ComplexDataTypeDef udt) {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.put(udt.getCode(), element.getLabelID());
        return map;
    }

    /**
     * @param reader
     * @param isAssoRef 是否关联带出
     * @param element
     * @param udt
     * @return
     */
    private static LinkedHashMap<String, String> getMultiColumnsUdtMap(ICefReader reader, boolean isAssoRef, IGspCommonElement element, ComplexDataTypeDef udt) {
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        for (IGspCommonField childElement : element.getChildElements()) {
            String udtElementId = element.getMappingRelation().getMappingInfo(childElement.getID());
            IGspCommonField udtElement = udt.findElement(udtElementId);
            //labeldId与Code不一致，说明是带出的 或者应该用ContainElements来判断
            //这个判断不准确
            if (isAssoRef) {
                //储粮存在手动修改 labelid和code不一致的情况（非带出多值UDT）
                if (childElement.getLabelID().equalsIgnoreCase(childElement.getCode())) {
                    map.put(udtElement.getLabelID(), childElement.getLabelID());
                } else {
                    map.put(udtElement.getLabelID(), childElement.getLabelID() + "_" + childElement.getCode());
                }
            } else {
                map.put(udtElement.getLabelID(), childElement.getLabelID());
            }

        }
        return map;
    }

    //endregion

    //region udt字段新增
    public static Object getUdtValue(GspBizEntityObject beObject, String labelId, String belongEleLabelId, Object udtData, boolean IsEnableEncryption) {

        var elementDic = beObject.getAllElementDic();
        if (!elementDic.containsKey(belongEleLabelId))
            return null;
        var parentElement = elementDic.get(belongEleLabelId);
//        var udtData = entityData.getValue(belongEleLabelId);
        UnifiedDataTypeDef udt = CMEngineUtil.getMetadataContent(parentElement.getUdtID());
        if (udt == null)
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2017, false,
                    parentElement.getLabelID(),
                    parentElement.getUdtID(),
                    ExceptionUtil.getBEInfo(beObject),
                    ExceptionUtil.getBizEntityObjInfo(beObject),
                    ExceptionUtil.getBizElementInfo(parentElement));

        if (udt instanceof SimpleDataTypeDef)
            return getUdtChangeValue(udtData, udt.getUdtType(), udt.getCode(), IsEnableEncryption);
        else {
            ComplexDataTypeDef complexUdt = (ComplexDataTypeDef) udt;
            switch (complexUdt.getDbInfo().getMappingType()) {
                case MultiColumns:
                    IGspCommonField udtElement = getUdtElement(parentElement, complexUdt, labelId);
                    return getUdtChangeValue(udtData, udt.getUdtType(), udtElement.getLabelID());
                case SingleColumn:
                    return getUdtChangeValue(udtData, udt.getUdtType(), udt.getCode());
            }
        }
        return null;
    }

    public static Object getUdtValue(GspBizEntityObject beObject, String labelId, String belongEleLabelId, Object udtData) {

        var elementDic = beObject.getAllElementDic();
        if (!elementDic.containsKey(belongEleLabelId))
            return null;
        var parentElement = elementDic.get(belongEleLabelId);
        UnifiedDataTypeDef udt = CMEngineUtil.getMetadataContent(parentElement.getUdtID());
        if (udt == null) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2017, false, parentElement.getLabelID(), parentElement.getUdtID(), ExceptionUtil.getBEInfo(beObject),
                    ExceptionUtil.getBizEntityObjInfo(beObject), ExceptionUtil.getBizElementInfo(parentElement));
        }

        if (udt instanceof SimpleDataTypeDef)
            return getUdtChangeValue(udtData, udt.getUdtType(), udt.getCode());
        else {
            ComplexDataTypeDef complexUdt = (ComplexDataTypeDef) udt;
            switch (complexUdt.getDbInfo().getMappingType()) {
                case MultiColumns:
                    IGspCommonField udtElement = getUdtElement(parentElement, complexUdt, labelId);
                    return getUdtChangeValue(udtData, udt.getUdtType(), udtElement.getLabelID());
                case SingleColumn:
                    return getUdtChangeValue(udtData, udt.getUdtType(), udt.getCode());
            }
        }
        return null;
    }

    public static IGspCommonField getUdtElement(IGspCommonField element, ComplexDataTypeDef udt, String columnName) {
        IGspCommonField childElement = findChildElementByLabelId(element, columnName);
        String udtElementId = element.getMappingRelation().getMappingInfo(childElement.getID());
        IGspCommonField udtElement = udt.findElement(udtElementId);
        if (udtElement == null)
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2033, udt.getCode(), udtElementId, udt.getID());
        return udtElement;
    }

    private static IGspCommonField findChildElementByLabelId(IGspCommonField element, String lableId) {
        for (IGspCommonField childElement : element.getChildElements()) {
            if (childElement.getLabelID().equals(lableId))
                return childElement;
        }
        throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2011, lableId,
                ExceptionUtil.getBEInfo((IGspCommonObject) element.getBelongObject()),
                ExceptionUtil.getBizEntityObjInfo((IGspCommonObject) element.getBelongObject()));
    }

    public static Object getUdtChangeValue(Object propertyValue, String configId, String propName) {
        if (propertyValue instanceof ValueObjModifyChangeDetail) {
            propertyValue = ((ValueObjModifyChangeDetail) propertyValue).getData();
        }
        if (!(propertyValue instanceof ICefData)) {
            return propertyValue;
        }
        return UdtManagerUtils.getUdtRepositoryFactory().createRepository(configId).getPersistenceValue(propName, (ICefData) propertyValue);
    }

    public static Object getUdtChangeValue(Object propertyValue, String configId, String propName, boolean IsEnableEncryption) {
        if (propertyValue instanceof ValueObjModifyChangeDetail) {
            propertyValue = ((ValueObjModifyChangeDetail) propertyValue).getData();
        }
        if (!(propertyValue instanceof ICefData)) {
            return encryptIfEnabled(propertyValue, IsEnableEncryption);
        }
        propertyValue = UdtManagerUtils.getUdtRepositoryFactory().createRepository(configId).getPersistenceValue(propName, (ICefData) propertyValue);
        propertyValue = encryptIfEnabled(propertyValue, IsEnableEncryption);

        return propertyValue;
    }
    //endregion

    //region udt字段更新

//    public static Object getUdtModifyValue(GspBizEntityObject beObject, String labelId, String belongEleLabelId, Object udtChange){
//
//        var elementDic = beObject.getAllElementDic();
//        if(!elementDic.containsKey(belongEleLabelId))
//            return null;
//        var parentElement = elementDic.get(belongEleLabelId);
////        var udtData = entityData.getValue(belongEleLabelId);
//
//        GspMetadata udtMetadata = getCustomizationRtService().getMetadata(parentElement.getUdtID());
//        if (udtMetadata == null)
//            throw new RuntimeException("没有找到业务字段元数据：" + parentElement.getUdtID());
//        UnifiedDataTypeDef udt = (UnifiedDataTypeDef) udtMetadata.getContent();
//
//        if (udt instanceof SimpleDataTypeDef)
//            return getUdtChangeValue(udtData, udt.getUdtType(), udt.getCode());
//        else{
//            ComplexDataTypeDef complexUdt = (ComplexDataTypeDef)udt;
//            switch (complexUdt.getDbInfo().getMappingType()) {
//                case MultiColumns:
//                    IGspCommonField udtElement =getUdtElement(parentElement, complexUdt, labelId);
//                    return getUdtChangeValue(udtData, udt.getUdtType(), udtElement.getLabelID());
//                case SingleColumn:
//                    return getUdtChangeValue(udtData, udt.getUdtType(), udt.getCode());
//            }
//        }
//        return null;
//    }


    //endregion

}
